//
//  ZQQSqliteModelTool.m
//  sqlite的封装
//
//  Created by MoDao-iOS on 2017/7/24.
//  Copyright © 2017年 MoDao-iOS. All rights reserved.
//

#import "ZQQSqliteModelTool.h"
#import "ZQQModelTool.h"
#import "ZQQSqliteTool.h"
#import "ZQQTableTool.h"

@implementation ZQQSqliteModelTool

// 关于这个工具类的封装
// 实现方案 2
// 1. 基于配置
// 2. runtime动态获取
+ (BOOL)createTable:(Class)cls uid:(NSString *)uid {
    // 1. 创建表格的sql语句给拼接出来
    // 尽可能多的, 能够自己获取, 就自己获取, 实在判定不了用的意图的, 只能让用户来告诉我们
    
    // create table if not exists 表名(字段1 字段1类型, 字段2 字段2类型 (约束),...., primary key(字段))
    // 1.1 获取表格名称
    NSString *tableName = [ZQQModelTool tableName:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要操作这个模型, 必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    
    NSString *primaryKey = [cls primaryKey];
    
    // 1.2 获取一个模型里面所有的字段, 以及类型
    NSString *createTableSql = [NSString stringWithFormat:@"create table if not exists %@(%@, primary key(%@))", tableName, [ZQQModelTool columnNamesAndTypesStr:cls], primaryKey];
    
    // 2. 执行
    return [ZQQSqliteTool deal:createTableSql uid:uid];
}

+ (BOOL)isTableRequiredUpdate:(Class)cls uid:(NSString *)uid {
    NSArray *moduleNames = [ZQQModelTool allTableSortedIvarNames:cls];
    NSArray *tableNames = [ZQQTableTool tableSortedColumnNames:cls uid:uid];
    
    return ![moduleNames isEqualToArray:tableNames];
}

+ (BOOL)updateTable:(Class)cls uid:(NSString *)uid {
    // 1. 创建一个拥有正确结构的临时表
    // 1.1 获取表格名称
    NSString *tmpTableName = [ZQQModelTool tmpTableName:cls];
    NSString *tableName = [ZQQModelTool tableName:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要操作这个模型, 必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    
    // 1.2.如果临时表存在，先删除临时表
    NSString *deleteTmpTable = [NSString stringWithFormat:@"drop table if exists %@", tmpTableName];
    [ZQQSqliteTool deal:deleteTmpTable uid:uid];
    
    // 所有需要操作的语句数组
    NSMutableArray *execSqls = [NSMutableArray array];
    
    // 1.2 创建临时表格
    NSString *primaryKey = [cls primaryKey];
    NSString *createTableSql = [NSString stringWithFormat:@"create table if not exists %@(%@, primary key(%@));", tmpTableName, [ZQQModelTool columnNamesAndTypesStr:cls], primaryKey];
    [execSqls addObject:createTableSql];
    
    // 2. 根据主键, 插入数据
    // insert into xmgstu_tmp(stuNum) select stuNum from xmgstu;
    NSString *insertPrimaryKeyData = [NSString stringWithFormat:@"insert into %@(%@) select %@ from %@;", tmpTableName, primaryKey, primaryKey, tableName];
    [execSqls addObject:insertPrimaryKeyData];
    
    // 3. 根据主键, 把所有的数据更新到新表里面
    NSArray *oldNames = [ZQQTableTool tableSortedColumnNames:cls uid:uid];
    NSArray *newNames = [ZQQModelTool allTableSortedIvarNames:cls];
    
    // 4. 获取更名字典
    NSDictionary *changeNameDic = nil;
    if ([cls respondsToSelector:@selector(newNameToOldNameDic)]) {
        // @"age": @"age2"
        changeNameDic = [cls newNameToOldNameDic];
    }
    
    for (NSString *columnName in newNames) {
        // 旧字段名称
        NSString *oldName = columnName;
        // 找映射的旧的字段名称
        if ([changeNameDic[columnName] length] > 0) {
            oldName = changeNameDic[columnName];
        }
        
        if ((![oldNames containsObject:columnName] && ![oldNames containsObject:oldName]) || [columnName isEqualToString:primaryKey]) {
            continue;
        }
        // update 临时表 set 新字段名称 = （select 旧字段名 from 旧表格 where 临时表.主键 = 旧表格.主键）
        NSString *updateSql = [NSString stringWithFormat:@"update %@ set %@ = (select %@ from %@ where %@.%@ = %@.%@)", tmpTableName, columnName, oldName, tableName, tmpTableName, primaryKey, tableName, primaryKey];
        [execSqls addObject:updateSql];
    }
    // 5.删除老表
    NSString *deleteOldTable = [NSString stringWithFormat:@"drop table if exists %@", tableName];
    [execSqls addObject:deleteOldTable];
    
    // 6. 更新表名操作
    NSString *renameTableName = [NSString stringWithFormat:@"alter table %@ rename to %@", tmpTableName, tableName];
    [execSqls addObject:renameTableName];
    
    // 7. sql语句执行
    return [ZQQSqliteTool dealSqls:execSqls uid:uid];
}

+ (BOOL)saveOrUpdateModel:(id)model uid:(NSString *)uid {
    
    // 如果用户在使用过程中, 直接调用这个方法, 去保存模型
    // 保存一个模型
    Class cls = [model class];
    // 1. 判断表格是否存在, 不存在, 则创建
    if (![ZQQTableTool isTableExists:cls uid:uid]) {
        [self createTable:cls uid:uid];
    }
    
    // 2. 检测表格是否需要更新, 需要, 更新
    if ([self isTableRequiredUpdate:cls uid:uid]) {
        [self updateTable:cls uid:uid];
    }
    
    // 3. 判断记录是否存在, 主键
    // 从表格里面, 按照主键, 进行查询该记录, 如果能够查询到
    NSString *tableName = [ZQQModelTool tableName:cls];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要操作这个模型, 必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
    
    NSString *checkSql = [NSString stringWithFormat:@"select * from %@ where %@ = '%@'", tableName, primaryKey, primaryValue];
    NSArray *result = [ZQQSqliteTool querySql:checkSql uid:uid];
    
    // 获取字段数组
    NSArray *columnNames = [ZQQModelTool classIvarNameTypeDic:cls].allKeys;
    
    // 获取值数组
    // model keyPath:
    NSMutableArray *values = [NSMutableArray array];
    for (NSString *columnName in columnNames) {
        id value = [model valueForKeyPath:columnName];
        
        // 处理值是字典或者数组
        if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
            // 在这里, 把字典或者数组, 处理成为一个字符串, 保存到数据库里面去
            
            // 字典/数组 -> data
            NSData *data = [NSJSONSerialization dataWithJSONObject:value options:NSJSONWritingPrettyPrinted error:nil];
            // data -> nsstring
            value = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }
        
        [values addObject:value];
    }
    
    // 拼接字符串  字段1=字段1值 。。。
    NSInteger count = columnNames.count;
    NSMutableArray *setValueArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        NSString *name = columnNames[i];
        id value = values[i];
        NSString *setStr = [NSString stringWithFormat:@"%@='%@'",name, value];
        [setValueArray addObject:setStr];
    }
    
    // 字段名称, 字段值
    // update 表名 set 字段1=字段1值,字段2=字段2的值... where 主键 = '主键值'
    NSString *execSql = nil;
    if (result.count > 0) { // 更新
        execSql = [NSString stringWithFormat:@"update %@ set %@ where %@ = '%@'", tableName, [setValueArray componentsJoinedByString:@","], primaryKey, primaryValue];
   } else { // 插入
       // insert into 表名(字段1, 字段2, 字段3) values ('值1', '值2', '值3')
       // '   值1', '值2', '值3'
       execSql = [NSString stringWithFormat:@"insert into %@(%@) values ('%@')", tableName, [columnNames componentsJoinedByString:@","], [values componentsJoinedByString:@"','"]];
   }
    
    return [ZQQSqliteTool deal:execSql uid:uid];
}

+ (BOOL)deleteModel:(id)model uid:(NSString *)uid {
    Class cls = [model class];
    NSString *tableName = [ZQQModelTool tableName:cls];
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要操作这个模型, 必须要实现+ (NSString *)primaryKey;这个方法, 来告诉我主键信息");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryValue = [model valueForKeyPath:primaryKey];
    NSString *deleteSql = [NSString stringWithFormat:@"delete from %@ where %@ = '%@'", tableName, primaryKey,primaryValue];
    
    return [ZQQSqliteTool deal:deleteSql uid:nil];
}

+ (BOOL)deleteModel:(Class)cls whereStr:(NSString *)whereStr uid:(NSString *)uid {
    NSString *tableName = [ZQQModelTool tableName:cls];
    
    NSString *deleteSql = [NSString stringWithFormat:@"delete from %@", tableName];
    if (whereStr.length > 0) {
        deleteSql = [deleteSql stringByAppendingFormat:@" where %@", whereStr];
    }
    
    return [ZQQSqliteTool deal:deleteSql uid:uid];
}

+ (BOOL)deleteModel:(Class)cls columnName:(NSString *)name relation:(SqlExpressionRelationType)relation value:(id)value uid:(NSString *)uid {
    NSString *tableName = [ZQQModelTool tableName:cls];
    
    NSString *deleteSql = [NSString stringWithFormat:@"delete from %@ where %@ %@ '%@'", tableName, name, self.SqlExpressionRelationTypeDic[@(relation)], value];
    
    // 假定肯定传值
    return [ZQQSqliteTool deal:deleteSql uid:uid];
}

+ (BOOL)deleteModel:(Class)cls keys: (NSArray *)keys relations: (NSArray *)relations values: (NSArray *)values nao: (NSArray *)naos uid: (NSString *)uid {
    NSMutableString *resultStr = [NSMutableString string];
    
    for (int i = 0; i < keys.count; i++) {
        NSString *key = keys[i];
        NSString *relationStr = [self SqlExpressionRelationTypeDic][relations[i]];
        id value = values[i];
        
        NSString *tempStr = [NSString stringWithFormat:@"%@ %@ '%@'", key, relationStr, value];
        
        [resultStr appendString:tempStr];
        
        if (i != keys.count - 1) {
            NSString *naoStr = [self naoTypeSQLRelationDic][naos[i]];
            [resultStr appendFormat:@" %@ ", naoStr];
        }
    }
    
    NSString *tableName = [ZQQModelTool tableName:cls];
    NSString *sql = [NSString stringWithFormat:@"delete from %@ where %@", tableName, resultStr];
    return [ZQQSqliteTool deal:sql uid:uid];
}

+ (NSArray *)queryAllModels:(Class)cls uid:(NSString *)uid {
    NSString *tableName = [ZQQModelTool tableName:cls];
    
    // 1. sql
    NSString *sql = [NSString stringWithFormat:@"select *from %@", tableName];
    
    // 2. 执行查询,
    // key value
    // 模型的属性名称, 和属性值
    NSArray <NSDictionary *>*results = [ZQQSqliteTool querySql:sql uid:uid];
    
    // 3. 处理查询的结果集 -> 模型数组
    return [self parseResults: results withClass:cls];
}

+ (NSArray *)queryModels:(Class)cls columnName:(NSString *)name relation:(SqlExpressionRelationType)relation value:(id)value uid:(NSString *)uid {
    NSString *tableName = [ZQQModelTool tableName:cls];
    // 1. 拼接sql语句
    NSString *sql = [NSString stringWithFormat:@"select * from %@ where %@ %@ '%@'",tableName, name, self.SqlExpressionRelationTypeDic[@(relation)], value];
    
    // 2. 查询结果集
    NSArray <NSDictionary *>*results = [ZQQSqliteTool querySql:sql uid:uid];
    
    return [self parseResults:results withClass:cls];
}

+ (NSArray *)queryModels:(Class)cls keys:(NSArray *)keys relations:(NSArray *)relations values:(NSArray *)values nao:(NSArray *)naos uid:(NSString *)uid {
    NSMutableString *resultStr = [NSMutableString string];
    
    for (int i = 0; i < keys.count; i++) {
        NSString *key = keys[i];
        NSString *relationStr = [self SqlExpressionRelationTypeDic][relations[i]];
        id value = values[i];
        
        NSString *tempStr = [NSString stringWithFormat:@"%@ %@ '%@'", key, relationStr, value];
        
        [resultStr appendString:tempStr];
        
        if (i != keys.count - 1) {
            NSString *naoStr = [self naoTypeSQLRelationDic][naos[i]];
            [resultStr appendString:[NSString stringWithFormat:@" %@ ", naoStr]];
        }
    }
    
    NSString *tableName = [ZQQModelTool tableName:cls];
    NSString *sql = [NSString stringWithFormat:@"select * from %@ where %@", tableName, resultStr];
    NSArray *rowDicArray = [ZQQSqliteTool querySql:sql uid:uid];
    NSArray *resultM = [self parseResults:rowDicArray withClass:cls];
    
    return resultM;
}

+ (NSArray *)queryModels:(Class)cls sql:(NSString *)sql uid:(NSString *)uid {
    NSArray *array = [ZQQSqliteTool querySql:sql uid:uid];
    
    NSMutableArray *models = [NSMutableArray array];
    for (NSDictionary *dic in array) {
        id model = [[cls alloc] init];
        [model setValuesForKeysWithDictionary:dic];
        [models addObject:model];
    }
    return models;
}


+ (NSArray *)parseResults:(NSArray <NSDictionary *>*)results withClass:(Class)cls {
    // 1. 处理查询结果集
    NSMutableArray *models = [NSMutableArray array];
    
    // 2. 获取属性名称 -》 类型
    NSDictionary *nameTypeDic = [ZQQModelTool classIvarNameTypeDic:cls];
    
    for (NSDictionary *modelDic in results) {
        id model = [[cls alloc] init];
        [models addObject:model];
        
        [modelDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            // 处理属性类型是字典或者数组的情况
            NSString *type = nameTypeDic[key];
            id resultValue = obj;
            
            if ([type isEqualToString:@"NSArray"] || [type isEqualToString:@"NSDictionary"]) {
                NSData *data = [obj dataUsingEncoding:NSUTF8StringEncoding];
                resultValue = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
            } else if ([type isEqualToString:@"NSMutableArray"] || [type isEqualToString:@"NSMutableDictionary"]) {
                NSData *data = [obj dataUsingEncoding:NSUTF8StringEncoding];
                resultValue = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
            }
            
            [model setValue:resultValue forKey:key];
        }];
    }
    
    return models;
}

// 关系映射表
+ (NSDictionary *)SqlExpressionRelationTypeDic {
    return @{
             @(SqlExpressionRelationTypeMore):@">",
             @(SqlExpressionRelationTypeLess):@"<",
             @(SqlExpressionRelationTypeEqual):@"=",
             @(SqlExpressionRelationTypeMoreEqual):@">=",
             @(SqlExpressionRelationTypeLessEqual):@"<=",
             @(SqlExpressionRelationTypeNotEqual): @"!="
             };
}

+ (NSDictionary *)naoTypeSQLRelationDic {
    return @{
             @(ZQQSqliteModeToolNAONot): @"not",
             @(ZQQSqliteModeToolNAOAnd): @"and",
             @(ZQQSqliteModeToolNAOOr): @"or"
             };
}

@end
